package com.hmall.trade.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.CartClient;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.config.mq.MultiDelayMessage;
import com.hmall.common.config.mq.UserInfoMessageProcessor;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.MqHelper;
import com.hmall.common.utils.UserContext;
import com.hmall.trade.constants.MqConstants;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.po.Order;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author: liuhongjun
 * @since: 2024-05-14
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    private final ItemClient itemClient;
    private final IOrderDetailService detailService;
    private final CartClient cartClient;
    private final RabbitTemplate rabbitTemplate;
    private final MqHelper mqHelper;

    @Override
    @GlobalTransactional
    public Long createOrder(OrderFormDTO orderFormDTO) {
        // 1.订单数据
        Order order = new Order();
        // 1.1.查询商品
        List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
        // 1.2.获取商品id和数量的Map
        Map<Long, Integer> itemNumMap = detailDTOS.stream().collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
        Set<Long> itemIds = itemNumMap.keySet();
        // 1.3.查询商品
        List<ItemDTO> items = this.itemClient.queryItemByIds(itemIds);
        if (items == null || items.size() < itemIds.size()) {
            throw new BadRequestException("商品不存在");
        }
        // 1.4.基于商品价格、购买数量计算商品总价：totalFee
        int total = 0;
        for (ItemDTO item : items) {
            total += item.getPrice() * itemNumMap.get(item.getId());
        }
        order.setTotalFee(total);
        // 1.5.其它属性
        order.setPaymentType(orderFormDTO.getPaymentType());
        order.setUserId(UserContext.getUser());
        order.setStatus(1);
        order.setCloseTime(LocalDateTime.now().plusMinutes(15));
        // 1.6.将Order写入数据库order表中
        super.save(order);

        // 2.保存订单详情
        List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
        this.detailService.saveBatch(details);

        // 3.清理购物车商品
        //this.cartClient.removeByItemIds(itemIds);
        try {
            // 3.1.获取当前用户信息
            Long userId = UserContext.getUser();
            // 3.2.设置消息属性请求头
            UserInfoMessageProcessor userInfoMessageProcessor = new UserInfoMessageProcessor(userId);
            this.rabbitTemplate.convertAndSend("trade.topic", "order.create", itemIds, userInfoMessageProcessor);
        } catch (Exception e) {
            log.error("清理购物车商品的消息发送失败，商品id集合：{}", itemIds, e);
        }

        // 4.扣减库存
        try {
            this.itemClient.updateStock(detailDTOS);
        } catch (Exception e) {
            throw new RuntimeException("库存不足！");
        }
        // 5.发送MQ消息，延迟查询订单支付状态
        try {
            // 5.1.组织消息，延迟时长为：10秒(6次)、20秒(3次)、1分钟(3次)、5分钟、10分钟、15分钟共15次延迟查询
            MultiDelayMessage<Long> msg = MultiDelayMessage.of(order.getId(),
                    10000L, 10000L, 10000L, 10000L, 10000L, 10000L, 20000L, 20000L, 20000L,
                    600000L, 600000L, 600000L, 3000000L, 6000000L, 9000000L);
            // 5.2.发送延迟消息
            this.rabbitTemplate.convertAndSend(MqConstants.DELAY_EXCHANGE, MqConstants.DELAY_ORDER_ROUTING_KEY, msg);
            //this.mqHelper.sendDelayMessage(MqConstants.DELAY_EXCHANGE, MqConstants.DELAY_ORDER_ROUTING_KEY, order.getId(), 600000);
        } catch (AmqpException e) {
            log.error("消息发送异常", e);
        }
        return order.getId();
    }

    /**
     * 高并发出现线程安全问题如何解决？乐观锁
     * @param orderId 订单id
     */
    @Override
    @Transactional
    public void markOrderPaySuccess(Long orderId) {
        // UPDATE `order` SET status = ? , pay_time = ? WHERE id = ? AND status = 1
        super.lambdaUpdate().set(Order::getStatus, 2).set(Order::getPayTime, LocalDateTime.now()).eq(Order::getId, orderId).eq(Order::getStatus, 1) //订单状态的乐观锁判断
                .update();
        // 1.查询订单
        /*Order oldOrder = super.getById(orderId);
        // 2.判断订单状态
        if (oldOrder == null || oldOrder.getStatus() != 1) {
            // 订单不存在或者订单状态不是1，放弃处理
            return;
        }
        // 3.尝试更新订单
        Order order = new Order();
        order.setId(orderId);
        order.setStatus(2);
        order.setPayTime(LocalDateTime.now());
        updateById(order);*/
    }

    private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
        List<OrderDetail> details = new ArrayList<>(items.size());
        for (ItemDTO item : items) {
            OrderDetail detail = new OrderDetail();
            detail.setName(item.getName());
            detail.setSpec(item.getSpec());
            detail.setPrice(item.getPrice());
            detail.setNum(numMap.get(item.getId()));
            detail.setItemId(item.getId());
            detail.setImage(item.getImage());
            detail.setOrderId(orderId);
            details.add(detail);
        }
        return details;
    }

    @Override
    public void cancelOrder(Long orderId) {
        // 1.修改订单状态为已关闭
        super.lambdaUpdate().set(Order::getStatus, 5).eq(Order::getStatus, 1).eq(Order::getId, orderId).update();
        // 2.封装商品库存信息
        List<OrderDetail> detailList = this.detailService.lambdaQuery().eq(OrderDetail::getOrderId, orderId).list();
        ArrayList<OrderDetailDTO> detailDTOList = new ArrayList<>(detailList.size());
        detailList.forEach(n -> {
            Long itemId = n.getItemId();
            Integer num = n.getNum() * (-1);
            OrderDetailDTO dto = new OrderDetailDTO();
            dto.setItemId(itemId);
            dto.setNum(num);
            detailDTOList.add(dto);
        });
        // 3.更新商品库存信息
        this.itemClient.updateStock(detailDTOList);
    }

}
